package model

import (
	janitor "github.com/json-iterator/go"
	"github.com/pkg/errors"
	"github.com/tal-tech/go-zero/core/logx"
	"strconv"
)

var json = janitor.ConfigCompatibleWithStandardLibrary

// 节点类型
const (
	NtStart NodeType = "start" // 开始节点
	// NtCondition 条件节点：条件节点没有属性，只是表示下一个节点需要判断进入条件
	NtCondition NodeType = "condition" // 条件节点
	NtTask      NodeType = "task"      // 任务节点
	NtEnd       NodeType = "end"       // 结束节点
)

// 处理人类型
const (
	AtUser    ActorType = "user"    // 处理人为用户
	AtRole    ActorType = "role"    // 处理人为角色/职位
	AtManager ActorType = "manager" // 处理人为主管
)

// NodeType 节点类型
type NodeType string

// ActorType 处理人类型
type ActorType string

// ProcDefNode 流程定义节点
type ProcDefNode struct {
	ViewId string   `json:"id,omitempty"`   // 节点视图id
	Type   NodeType `json:"type,omitempty"` // 节点类型
	// 进入此节点的条件: 当 Type 不是 NT_SINGLE_TASK 或 NT_MULTI_TASK 时 Prop 为空
	Condition *Condition       `json:"condition,omitempty"`
	Prop      *ProcDefNodeProp `json:"prop,omitempty"`       // 节点属性
	NextNodes []ProcDefNode    `json:"next_nodes,omitempty"` // 后续节点
}

func (m *ProcDefNode) Parse(str string) error {
	if err := json.Unmarshal([]byte(str), m); err != nil {
		return err
	}
	return nil
}

func (m *ProcDefNode) ToJsonStr() string {
	value, err := json.Marshal(m)
	if err != nil {
		return ""
	}
	return string(value)
}

// Condition 条件
type Condition struct {
	Field string `json:"field,omitempty"` // 比较的字段，必须在流程表单中存在的字段
	Op    string `json:"op,omitempty"`    // 比较运算符
	Value string `json:"value,omitempty"` // 比较字段的值
}

// Match 配备方法，判断表达式是否成立, 当操作符是 ==、!= 的时候用字符串比较，
// 当操作符是 >=、<=、>、<时用数值比较
// 只支持 == != >= <= > < 这几个操作符，其他一切操作符均返回false
func (m *Condition) Match(value string) bool {
	// 判断
	switch m.Op {
	case "==": // 如果是的 == 则用字符串比对
		return m.Value == value
	case "!=": // 如果是 != 则用字符串比对
		return m.Value != value
	case ">=": // 如果是 >= 则转换成数值比对
		v1, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return false
		}
		v2, err := strconv.ParseFloat(m.Value, 64)
		if err != nil {
			return false
		}
		return v1 >= v2
	case "<=": // 如果是 <= 则转换成数值比对
		v1, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return false
		}
		v2, err := strconv.ParseFloat(m.Value, 64)
		if err != nil {
			return false
		}
		return v1 <= v2
	case ">": // 如果是 > 则转换成数值比对
		v1, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return false
		}
		v2, err := strconv.ParseFloat(m.Value, 64)
		if err != nil {
			return false
		}
		return v1 > v2
	case "<": // 如果是 < 则转换成数值比对
		v1, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return false
		}
		v2, err := strconv.ParseFloat(m.Value, 64)
		if err != nil {
			return false
		}
		return v1 < v2
	}
	return false
}

// ProcDefNodeProp 节点的属性
type ProcDefNodeProp struct {
	Name   string         `json:"name,omitempty"`   // 节点名称
	Key    string         `json:"key,omitempty"`    // 节点key
	Desc   string         `json:"desc,omitempty"`   // 节点描述
	Actors []ProcDefActor `json:"actors,omitempty"` // 处理人
	// 最小通过人数：至少有 MinNumOfPasser 个人同意，流程才能继续执行。默认为 Actors 的长度
	MinNumOfPasser int `json:"min_num_of_passer,omitempty"`
}

// ProcDefActor 处理人(流程定义)
type ProcDefActor struct {
	Id string `json:"id,omitempty"` // 处理人id
	// 处理人类型：当处理人类型是 AtUser 或 AtRole 时， Id 表示用户id 和 角色/职位id；
	// 当处理人类型是 AtManager 时 Id 表示主管离你的距离， 0表示直接主管，1表示上一级主管，以此类推
	Type ActorType `json:"type,omitempty"` // 处理者类型
	Name string    `json:"name,omitempty"` // 处理人名称
}

// ProcExecNode 流程执行节点
type ProcExecNode struct {
	Type        NodeType        `json:"type,omitempty"`         // type
	Name        string          `json:"name,omitempty"`         // 节点名称
	Key         string          `json:"key,omitempty"`          // 业务key
	Desc        string          `json:"desc,omitempty"`         // 节点描述
	Actors      []ProcExecActor `json:"actors,omitempty"`       // 处理人
	ActorsCount int             `json:"actors_count,omitempty"` // 处理人个数
	// 最小通过人数：至少有 MinNumOfPasser 个人同意，流程才能继续执行。默认为 Actors 的长度
	MinNumOfPasser int `json:"min_num_of_passer,omitempty"`
}

// Parse json字符串转 ProcExecNode对象
func (m *ProcExecNode) Parse(jsonStr string) error {
	if err := json.Unmarshal([]byte(jsonStr), m); err != nil {
		return errors.Wrap(err, "ProcExecNode反序列化失败")
	}
	return nil
}

// ToJsonStr 转成json字符串
func (m *ProcExecNode) ToJsonStr() string {
	str, err := json.Marshal(m)
	if err != nil {
		logx.Error(errors.Wrap(err, "ProcExecNode to json string failed"))
		return ""
	}
	return string(str)
}

// ProcExecActor 处理人(执行流)
type ProcExecActor struct {
	Id   string `json:"id,omitempty"`   // 处理人id
	Name string `json:"name,omitempty"` // 处理人名称
}
